package com.xiongtian.springframework.beans.factory.support;

import com.xiongtian.springframework.beans.BeansException;
import com.xiongtian.springframework.beans.factory.FactoryBean;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: xiongtian
 * @CreateTime: 2025-04-11  10:57
 * @Version: 1.0
 * @Description: 工厂Bean注册支持类
 * 实现了FactoryBeanRegistry接口，用于注册FactoryBean
 * 提供了缓存FactoryBean创建的对象的功能
 */

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    /**
     * 缓存FactoryBean创建的对象
     * 用于单例模式下的缓存
     * 防止重复创建对象
     */
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();

    /**
     * 获取缓存中的对象
     * @param beanName
     * @return
     */
    protected Object getCachedObjectForFactoryBean(String beanName) {
        Object object = factoryBeanObjectCache.get(beanName);
        return (NULL_OBJECT != object ? object : null);
    }

    /**
     * 获取对象
     * 如果是FactoryBean，调用getObject()方法
     * 1.不是FactoryBean，直接返回
     * 2.是FactoryBean，判断是否是单例
     * 3.是单例，从缓存中获取
     * 4.不是单例，直接调用getObject()方法
     * 5.将实例放入缓存中
     * 6.返回实例
     *
     * @param factory
     * @param beanName
     * @return
     */
    protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {
        if (factory.isSingleton()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (null == object) {
                object = doGetObjectFromFactoryBean(factory, beanName);
                this.factoryBeanObjectCache.put(beanName, (null != object ? object : NULL_OBJECT));
            }
            return (null != object ? object : NULL_OBJECT);
        } else {
            return doGetObjectFromFactoryBean(factory, beanName);
        }
    }

    /**
     * 获取对象
     * 通过FactoryBean的getObject方法获取对象
     * 如果FactoryBean抛出异常，抛出BeansException
     *
     * @param factory FactoryBean对象
     * @param beanName Bean名称
     * @return 对象
     */
    private Object doGetObjectFromFactoryBean(FactoryBean factory, String beanName) {
        try {
            return factory.getObject();
        } catch (Exception e) {
            throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", e);
        }
    }


}
